home *** CD-ROM | disk | FTP | other *** search
- /* POP2 Server state machine - see RFC 937
- *
- * also see other credits in pop2cli.c
- * 10/89 Mike Stockett wa7dyx
- * Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
- * Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
- * some code streaming - DB3FL.911111
- */
-
- #include <stdio.h>
- #include <time.h>
- #include <sys/stat.h>
- #ifdef UNIX
- #include <sys/types.h>
- #endif
- #if defined(__STDC__) || defined(__TURBOC__)
- #include <stdarg.h>
- #endif
- #include <ctype.h>
- #include <setjmp.h>
- #include "global.h"
- #include "config.h"
- #ifdef POP2_SERVER
- #include "mbuf.h"
- #include "cmdparse.h"
- #include "socket.h"
- #include "proc.h"
- #include "files.h"
- #include <fcntl.h>
-
- #undef LOG
-
- #define BITS_PER_WORD 16
- #define BUF_LEN 128
-
- /* mail separator */
- #define isSOM(x) ((strncmp(x,"From ",5) == 0))
-
- /* ---------------- common server data structures ---------------- */
-
- /* POP2 server control block */
-
- struct pop_scb {
- int socket; /* socket number for this connection */
- char state; /* server state */
- #define LSTN 0
- #define AUTH 1
- #define MBOX 2
- #define ITEM 3
- #define NEXT 4
- #define DONE 5
- char buf[BUF_LEN], /* input line buffer */
- count, /* line buffer length */
- username[64]; /* user/folder name */
- FILE *wf; /* work folder file pointer */
- int folder_len, /* number of msgs in current folder */
- msg_num; /* current msg number */
- long msg_len; /* length of current msg */
- int msg_status_size; /* size of the message status array */
- long curpos, /* current msg's position in file */
- folder_file_size, /* length of the current folder file, in bytes */
- nextpos; /* next msg's position in file */
- unsigned int folder_modified, /* mail folder contents modified flag */
- *msg_status; /* message status array pointer */
- };
-
- #define NULLSCB (struct pop_scb *)0
-
- /* Response messages */
-
- static char count_rsp[] = "#%d messages in this folder\n",
- error_rsp[] = "- ERROR: %s\n",
- greeting_msg[] = "+ POP2 %s\n",
- /* length_rsp[] = "=%ld bytes in this message\n", */
- length_rsp[] = "=%ld characters in Message #%d\n",
- msg_line[] = "%s\n",
- no_mail_rsp[] = "+ No mail, sorry\n",
- no_more_rsp[] = "=%d No more messages in this folder\n",
- signoff_msg[] = "+ Bye, thanks for calling\n";
-
-
- static int Spop = -1; /* prototype socket for service */
-
- /* Command string specifications */
- static char ackd_cmd[] = "ACKD",
- acks_cmd[] = "ACKS",
- #ifdef POP_FOLDERS
- fold_cmd[] = "FOLD ",
- #endif
- login_cmd[] = "HELO ",
- nack_cmd[] = "NACK",
- quit_cmd[] = "QUIT",
- read_cmd[] = "READ",
- retr_cmd[] = "RETR";
-
- static void near
- state_error(scb,msg)
- struct pop_scb *scb;
- char *msg;
- {
- usprintf(scb->socket,error_rsp,msg);
- scb->state = DONE;
- }
-
- static int near
- newmail(scb)
- struct pop_scb *scb;
- {
- char *folder_pathname;
- struct stat folder_stat;
- int s;
-
- folder_pathname = mxallocw(strlen(Mailspool) + strlen(scb->username) + 5);
- sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
- s = stat(folder_pathname,&folder_stat);
- xfree(folder_pathname);
-
- if(s) {
- state_error(scb,"Unable to get old mail folder's status");
- return(FALSE);
- } else {
- return((folder_stat.st_size > scb->folder_file_size) ? TRUE : FALSE);
- }
- }
-
- static int near
- isdeleted(scb,msg_no)
- struct pop_scb *scb;
- int msg_no;
- {
- unsigned int mask = 1, offset;
-
- msg_no--;
- offset = msg_no / BITS_PER_WORD;
- mask <<= msg_no % BITS_PER_WORD;
- return (((scb->msg_status[offset]) & mask) ? TRUE : FALSE);
- }
-
- static void near
- close_folder(scb)
- struct pop_scb *scb;
- {
- char folder_pathname[64], line[BUF_LEN];
- FILE *fd;
- int deleted = FALSE, msg_no = 0;
- struct stat folder_stat;
-
- if (scb->wf == NULL)
- return;
-
- if (!scb->folder_modified) {
- /* no need to re-write the folder if we have not modified it */
-
- fclose(scb->wf);
- scb->wf = NULL;
-
- xfree((char *)scb->msg_status);
- scb->msg_status = NULL;
- return;
- }
- sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
-
- if (newmail(scb)) {
- /* copy new mail into the work file and save the
- message count for later */
- if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE)
- return;
-
- fseek(scb->wf,0,SEEK_END);
- fseek(fd,scb->folder_file_size,SEEK_SET);
- while (!feof(fd)) {
- fgets(line,BUF_LEN,fd);
- fputs(line,scb->wf);
- }
-
- fclose(fd);
- }
- /* now create the updated mail folder */
- if ((fd = open_file(folder_pathname,"w",0,1)) == NULLFILE)
- return;
-
- rewind(scb->wf);
- while (!feof(scb->wf)){
- fgets(line,BUF_LEN,scb->wf);
-
- if (isSOM(line)){
- msg_no++;
- deleted = (msg_no <= scb->folder_len) ?
- isdeleted(scb,msg_no) : FALSE;
- }
- if (deleted)
- continue;
-
- fputs(line,fd);
- }
-
- fclose(fd);
-
- /* trash the updated mail folder if it is empty */
- if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
- unlink(folder_pathname);
-
- fclose(scb->wf);
- scb->wf = NULL;
- xfree((char *)scb->msg_status);
- scb->msg_status = NULL;
- }
-
- static void near
- do_cleanup(scb)
- struct pop_scb *scb;
- {
- close_folder(scb);
- scb->state = DONE;
- }
-
- static void near
- deletemsg(scb,msg_no)
- struct pop_scb *scb;
- int msg_no;
- {
- unsigned int mask = 1,offset;
-
- if (scb == NULLSCB) /* check for null -- wa6smn */
- return;
- msg_no--;
- offset = msg_no / BITS_PER_WORD;
- mask <<= msg_no % BITS_PER_WORD;
- scb->msg_status[offset] |= mask;
- scb->folder_modified = TRUE;
- }
-
- static void near
- print_message_length(scb)
- struct pop_scb *scb;
- {
- char *print_control_string;
-
- if (scb == NULLSCB) /* check for null -- wa6smn */
- return;
- if (scb->msg_len > 0 || scb->msg_num <= scb->folder_len)
- print_control_string = length_rsp;
- else
- print_control_string = no_more_rsp;
-
- usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
- }
-
- static void near
- get_message(scb,msg_no)
- struct pop_scb *scb;
- int msg_no;
- {
- char line[BUF_LEN], *cp;
-
- if (scb == NULLSCB) /* check for null -- wa6smn */
- return;
-
- scb->msg_len = 0;
-
- if (msg_no > scb->folder_len) {
- scb->curpos = 0;
- scb->nextpos = 0;
- return;
- } else {
- /* find the message and its length */
- rewind(scb->wf);
- while (!feof(scb->wf) && (msg_no > -1)) {
- if (msg_no > 0)
- scb->curpos = ftell(scb->wf);
-
- fgets(line,BUF_LEN,scb->wf);
- if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
- *cp = '\0';
-
- if (isSOM(line))
- msg_no--;
-
- if (msg_no != 0)
- continue;
-
- scb->nextpos = ftell(scb->wf);
- scb->msg_len += (strlen(line)+2); /* Add CRLF */
- }
- }
-
- if (scb->msg_len > 0)
- fseek(scb->wf,scb->curpos,SEEK_SET);
-
- /* we need the pointers even if the message was deleted */
- if(isdeleted(scb,scb->msg_num))
- scb->msg_len = 0;
- }
-
- static void near
- read_message(scb)
- struct pop_scb *scb;
- {
- if (scb == NULLSCB) /* check for null -- wa6smn */
- return;
-
- if (scb->buf[sizeof(read_cmd) - 1] == ' ')
- scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
- else
- scb->msg_num++;
-
- get_message(scb,scb->msg_num);
- print_message_length(scb);
- scb->state = ITEM;
- }
-
- static void near
- retrieve_message(scb)
- struct pop_scb *scb;
- {
- char line[BUF_LEN], *cp;
- long cnt;
-
- if (scb == NULLSCB) /* check for null -- wa6smn */
- return;
-
- if (scb->msg_len == 0) {
- state_error(scb,"Msg already deleted");
- return;
- }
- cnt = scb->msg_len;
-
- while(!feof(scb->wf) && (cnt > 0)) {
- fgets(line,BUF_LEN,scb->wf);
- if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
- *cp = '\0';
- usprintf(scb->socket,msg_line,line);
- cnt -= (strlen(line)+2); /* Compensate for CRLF */
- }
- scb->state = NEXT;
- }
-
- static void near
- open_folder(scb)
- struct pop_scb *scb;
- {
- char folder_pathname[64], line[BUF_LEN];
- FILE *fd;
- struct stat folder_stat;
-
- sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
- scb->folder_len = 0;
- scb->folder_file_size = 0;
-
- if (stat(folder_pathname,&folder_stat)){
- usputs(scb->socket,no_mail_rsp);
- return;
- }
- scb->folder_file_size = folder_stat.st_size;
- if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE)
- return;
-
- if ((scb->wf = temp_file(0,1)) == NULLFILE) {
- fclose(fd);
- return;
- }
- while(!feof(fd)) {
- fgets(line,BUF_LEN,fd);
-
- /* scan for begining of a message */
- if (isSOM(line))
- scb->folder_len++;
-
- /* now put the line in the work file */
- fputs(line,scb->wf);
-
- }
- fclose(fd);
-
- scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
-
- if ((((scb->folder_len) % BITS_PER_WORD) != 0)
- || (scb->msg_status_size == 0))
- scb->msg_status_size++;
-
- if ((scb->msg_status = (unsigned int *)cxallocw(scb->msg_status_size,
- sizeof(unsigned int))) == NULL) {
- state_error(scb,"Unable to create message status array");
- return;
- }
-
- usprintf(scb->socket,count_rsp,scb->folder_len);
-
- scb->state = MBOX;
- }
-
- #ifdef POP_FOLDERS
- static void near
- select_folder(scb)
- struct pop_scb *scb;
- {
- char *cp = scb->buf;
-
- while(*cp++ != ' ') ;
- strcpy(scb->username,cp);
-
- if (scb->wf != NULL)
- close_folder(scb);
- open_folder(scb);
- }
- #endif
-
- static int near
- poplogin(username,pass)
- char *pass;
- char *username;
- {
- char buf[BUF_LEN], *cp, *cp1;
- FILE *fp;
-
- if((fp = fopen(Popusers,READ_TEXT)) == NULLFILE) {
- /* User file doesn't exist */
- return(FALSE);
- }
-
- while(fgets(buf,BUF_LEN,fp),!feof(fp)) {
- if(buf[0] == '#')
- continue; /* Comment */
-
- if((cp = strchr(buf,':')) == NULLCHAR)
- /* Bogus entry */
- continue;
-
- *cp++ = '\0'; /* Now points to password */
- if(strcmp(username,buf) == 0)
- break; /* Found user name */
- }
-
- if(feof(fp)) {
- /* User name not found in file */
- fclose(fp);
- return(FALSE);
- }
- fclose(fp);
-
- if ((cp1 = strchr(cp,':')) == NULLCHAR)
- return(FALSE);
-
- *cp1 = '\0';
- if(strcmp(cp,pass) != 0) {
- /* Password required, but wrong one given */
-
- return(FALSE);
- }
- /* whew! finally made it!! */
- return(TRUE);
- }
-
- static void near
- pop_sm(scb)
- struct pop_scb *scb;
- {
- if (scb == NULLSCB) /* be certain it is good -- wa6smn */
- return;
-
- switch(scb->state) {
- case AUTH:
- if (strncmp(scb->buf,login_cmd,strlen(login_cmd)-2) == 0) {
- char password[40], *cp1, *cp = scb->buf;
- while(*cp++ != ' ') ;
- cp1 = cp;
- while(*++cp != ' ') ;
- *cp = '\0';
- strcpy(scb->username,cp1);
- strcpy(password,++cp);
- if (!poplogin(scb->username,password)) {
- #ifdef LOG
- log(scb->socket,"POP2 access DENIED to %s",scb->username);
- #endif
- state_error(scb,"Access denied");
- return;
- }
- #ifdef LOG
- log(scb->socket,"POP2 access granted to %s",scb->username);
- #endif
- open_folder(scb);
- } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
- do_cleanup(scb);
- } else
- state_error(scb,"(AUTH) Expected HELO or QUIT command");
- break;
-
- case MBOX:
- if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
- read_message(scb);
-
- #ifdef POP_FOLDERS
- else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
- select_folder(scb);
- #endif
- else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
- do_cleanup(scb);
- } else
- state_error(scb,
- #ifdef POP_FOLDERS
- "(MBOX) Expected FOLD, READ, or QUIT command");
- #else
- "(MBOX) Expected READ or QUIT command");
- #endif
- break;
-
- case ITEM:
- if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
- read_message(scb);
- #ifdef POP_FOLDERS
- else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
- select_folder(scb);
- #endif
- else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
- retrieve_message(scb);
- else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
- do_cleanup(scb);
- else
- state_error(scb,
- #ifdef POP_FOLDERS
- "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
- #else
- "(ITEM) Expected READ, RETR, or QUIT command");
- #endif
- break;
-
- case NEXT:
- if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
- /* ACKD processing */
- deletemsg(scb,scb->msg_num);
- scb->msg_num++;
- get_message(scb,scb->msg_num);
- } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
- /* ACKS processing */
- scb->msg_num++;
- get_message(scb,scb->msg_num);
- } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
- /* NACK processing */
- fseek(scb->wf,scb->curpos,SEEK_SET);
- } else {
- state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
- return;
- }
- print_message_length(scb);
- scb->state = ITEM;
- break;
-
- case DONE:
- /* do_cleanup(scb); now done by the pop process */
- break;
-
- default:
- state_error(scb,"(TOP) State Error!!");
- break;
- }
- }
-
- static void
- popserv(s,unused,p)
- int s;
- void *unused;
- void *p;
- {
- struct pop_scb *scb;
-
- sockowner(s,Curproc); /* We own it now */
- log(s,"POP2 open");
- sockmode(s,SOCK_ASCII);
-
- if((scb = (struct pop_scb *)mxallocw(sizeof(struct pop_scb))) == NULLSCB) {
- tputs(Nospace);
- close_s(s);
- return;
- }
- scb->folder_modified = FALSE;
- scb->socket = s;
- scb->state = AUTH;
-
- usprintf(scb->socket,greeting_msg,Hostname);
-
- loop:
- for(;;) {
- if (scb->state == DONE
- || (scb->count = recvline(s,scb->buf,BUF_LEN)) == -1) {
- /* He closed on us */
- break;
- }
- rip(scb->buf);
- if (*scb->buf == '\0') /* Ignore blank cmd lines */
- goto loop;
- pop_sm(scb);
- }
- do_cleanup(scb);
- if(scb->count != -1)
- usputs(scb->socket,signoff_msg);
- log(scb->socket,"POP2 close");
- close_s(scb->socket);
- if (scb->wf != NULL)
- fclose(scb->wf);
- if (scb->msg_status != NULL)
- xfree((char *)scb->msg_status);
- xfree((char *)scb);
-
- }
-
-
-
- /* ---------------------- POP start/stop cmds ---------------------- */
-
- /* Start up POP receiver service */
- int
- pop1(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- struct sockaddr_in lsocket;
- int s;
-
- if (Spop != -1)
- return 0;
-
- psignal(Curproc,0); /* Don't keep the parser waiting */
- chname(Curproc,"POP2 listener");
-
- lsocket.sin_family = AF_INET;
- lsocket.sin_addr.s_addr = INADDR_ANY;
- lsocket.sin_port = (argc < 2) ? IPPORT_POP2 : atoi(argv[1]);
-
- Spop = socket(AF_INET,SOCK_STREAM,0);
- bind(Spop,(char *)&lsocket,sizeof(lsocket));
- listen(Spop,1);
-
- for (;;) {
- if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
- break; /* Service is shutting down */
-
- if(availmem() < Memthresh){
- usputs(s,Nospace);
- shutdown(s,1);
- } else {
- sockmode(s,SOCK_ASCII);
- /* Spawn a server */
- newproc("POP2 server",1536,popserv,s,NULL,NULL,0);
- }
- }
- return 0;
- }
-
- /* Shutdown POP service (existing connections are allowed to finish) */
- int
- pop0(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- close_s(Spop);
- Spop = -1;
- return 0;
- }
-
- #endif /* POP2_SERVER */